/**
 * The Watering Control Module would control the amount of humidity in the soil
 * and when needed would open a valve connected to a water tube in order to water the plant.
 * This water tube is connected to a water tank whose level should also be controlled
 * in order to make sure there would also be enough water in it. In the case where the level
 * of water in the  tank is lower than a defined threshold an alarm would warn the user.
 * For this functionality we use four nodes.
 * One connected to a sensor board measuring the humidity in the soil.
 * One connected to a sensor board measuring the water level in the tank.
 * One connected to a stepper motor controlling the valve on the water tube. This mote is also used as the sink.
 * And a fourth one is used as a Gateway.
 *
 *
 * @author Paweł Pawłowski <abdul-pablo@tlen.pl>
 * @author Leily Zafari
 *
 **/


#include "watering.h"
#include "messages.h"
#include "mda100.h"
#include <math.h>

#if !(defined(SOIL_MODE) || defined(TANK_MODE) || defined(VALVE_MODE) || defined(GATEWAY_MODE)) || (defined(SOIL_MODE) && defined(TANK_MODE)) || (defined(SOIL_MODE) && defined(VALVE_MODE)) || (defined(SOIL_MODE) && defined(GATEWAY_MODE)) || (defined(TANK_MODE) && defined(VALVE_MODE)) || (defined(TANK_MODE) && defined(GATEWAY_MODE)) || (defined(VALVE_MODE) && defined(GATEWAY_MODE))
#error "You must compile with either -DSOIL_MODE or -DTANK_MODE or -DVALVE_MODE or -DGATEWAY_MODE"
#endif

module WateringControlM {
	uses
	{
		//basic interfaces
		interface Boot;
		interface Leds;

		//timers
		interface Timer<TMilli> as TimerSensor;
		interface Timer<TMilli> as TimerWateringControl;
		interface Timer<TMilli> as TimerSender;
		interface Timer<TMilli> as TimerResponse;

		//messages
		interface Packet as ValveControl_Packet;
		interface AMPacket as ValveControl_AMPacket;
		interface AMSend as ValveControl_AMSend;

		interface Packet as ValveState_Packet;
		interface AMPacket as ValveState_AMPacket;
		interface AMSend as ValveState_AMSend;
		interface Receive as ValveState_Receive;

		interface Packet as Humidity_Packet;
		interface AMPacket as Humidity_AMPacket;
		interface AMSend as Humidity_AMSend;
		interface Receive as Humidity_Receive;

		interface Packet as WaterLevel_Packet;
		interface AMPacket as WaterLevel_AMPacket;
		interface AMSend as WaterLevel_AMSend;
		interface Receive as WaterLevel_Receive;

		interface Packet as SystemStateToGateway_Packet;
		interface AMPacket as SystemStateToGateway_AMPacket;
		interface AMSend as SystemStateToGateway_AMSend;
		interface Receive as SystemStateToGateway_Receive;

		interface Packet as DataRequestToSensorNode_Packet;
		interface AMPacket as DataRequestToSensorNode_AMPacket;
		interface AMSend as DataRequestToSensorNode_AMSend;
		interface Receive as DataRequestToSensorNode_Receive;

		interface Packet as NoResponse_Packet;
		interface AMPacket as NoResponse_AMPacket;
		interface AMSend as NoResponse_AMSend;
		interface Receive as NoResponse_Receive;

		interface Packet as SystemAlertToGateway_Packet;
		interface AMPacket as SystemAlertToGateway_AMPacket;
		interface AMSend as SystemAlertToGateway_AMSend;
		interface Receive as SystemAlertToGateway_Receive;

		interface SplitControl as AMControl;
		interface SplitControl as SerialControl;
		interface Packet as SerialPacket;
		interface AMPacket as SerialAMPacket;

		interface AMSend as SystemStateToUSB_SerialMessageSend;
		interface AMSend as SystemAlertToUSB_SerialMessageSend;

		interface Receive as ValveControl_Receive;

		interface Receive as ValveControlToGateway_SerialReceive;
		interface Receive as DataRequestToGateway_SerialReceive;

		interface Receive as DataRequestToSink_Receive;
		interface Packet as DataRequestToSink_Packet;
		interface AMPacket as DataRequestToSink_AMPacket;
		interface AMSend as DataRequestToSink_AMSend;

		//sensors
		interface Read<double> as SensorRead;

		#ifdef VALVE_MODE
		//valve
		interface Valve;
		#endif

		//LKN sensor board
		interface SBoard;
	}
}

implementation {

	//communication interfaces state
	bool txBusy = FALSE;
	bool serialTxBusy = FALSE;

	//auxiliary parameters used for communication purposes -> see implementation
	bool publicMeasurement = FALSE;
	bool publicMeasurementDataToSerial = FALSE;
	bool publicMeasurementDataToGateway = FALSE;
	uint8_t collectedData; //2^0->humidity, 2^1->water/tank
	uint8_t alert_type = NO_ALERT; //255;

	//message packets
	message_t pkt;
	message_t serial_pkt;
	//system state
	#ifdef VALVE_MODE
	double humidity = SOIL_HUMIDITY_THRESHOLD + 1;
	double water = WARN_WATER_LEVEL + 1;
	uint8_t valve = 0;
	#else
	double humidity = 10.0;
	double water = 10.0;
	uint8_t valve = 0;
	#endif
	double sensorData;

	//saved received messages
	DataRequestMsgToSink* data_request_msg_to_sink;
	DataRequestMsgToSensorNode* data_request_msg_to_sensor_node;
	DataRequestMsgToGateway* data_request_msg_to_gateway;
	ValveControlMsgToGateway* valve_control_to_gateway;
	SystemAlertMsgToGateway* system_alert_msg_to_gayeway;

	//tasks declarations
	task void sendMeasurement();
	task void controlWatering();
	task void conductMeasurement();
	task void sendMeasurementDataToGateway();
	task void sendMeasurementDataToSerial();
	task void sendSystemAlertMsgToSerial();
	task void gatherMeasurementDataFromSensorNodes();
	task void gatherMeasurementDataFromSink();

	/**
	 * Converts value of water level value from double into unsigned integer.
	 * Used for message transmission purposes as doubles are not support by messaging of TinyOS.
	 *
	 * @param val double Value of water level represented by double
	 * @return uint32_t Value of water level represented by integer
	 */
	inline uint32_t convWater_uint32_t(double val)
	{
	    uint32_t var;
		//assumption: 0 <= val 99, max for uint32_t is 4 294 967 295
		val *= 1000;
		var = ((uint32_t)(ceil(fabs(val))));
		return var; //*1000;
	}

	/**
	 * Converts value of water level value from unsigned integer into double.
	 * Used for message transmission purposes as doubles are not support by messaging of TinyOS.
	 *
	 * @param val uint32_t Value of water level represented by integer
	 * @return double Value of water level represented by double
	 */
	inline double convWater_double(uint32_t val)
	{
		return (double) val / 1000;
	}

	/**
	 * Converts value of humidity value from double into unsigned integer.
	 * Used for message transmission purposes as doubles are not support by messaging of TinyOS.
	 *
	 * @param val double Value of humidity represented by double
	 * @return uint32_t Value of humidity represented by integer
	 */
	inline uint32_t convHumidity_uint32_t(double val)
	{
		uint32_t var;
		val *= 1000;
		var = ((uint32_t)(ceil(fabs(val))));
		return var;
	}

	/**
	 * Converts value of humidity value from unsigned integer into double.
	 * Used for message transmission purposes as doubles are not support by messaging of TinyOS.
	 *
	 * @param val uint32_t Value of humidity represented by integer
	 * @return double Value of humidity represented by double
	 */
	inline double convHumidity_double(uint32_t val)
	{
		return (double) val / 1000;
	}

	/**
	 * @brief Initializes the system.
	 *
	 * @details AMControl and SerialControl are started to enable the
	 * communication over the radio interface as well as the
	 * serial interface.
	 * Timers for sensing/control processes are started.
	 *
	 * @return void
	 **/
	event void Boot.booted()
	{
		call SBoard.init();
		call SerialControl.start();
		call AMControl.start();

		valve = 0;

		#ifdef SOIL_MODE
		call TimerSensor.startPeriodic(TIMER_MOISTURE_LEVEL_MEASUREMENT);
		call TimerSender.startPeriodic(TIMER_SENDER_S);
		call SBoard.greenOn();
		#endif

		#ifdef TANK_MODE
		call TimerSensor.startPeriodic(TIMER_WATER_LEVEL_MEASUREMENT);
		call TimerSender.startPeriodic(TIMER_SENDER_T);
		call SBoard.greenOn();
		#endif

		#ifdef VALVE_MODE
		call TimerWateringControl.startPeriodic(TIMER_WATERING_CONTROL);
		call TimerSender.startPeriodic(TIMER_SENDER_V);
		call SBoard.greenOn();
		#endif
	}



//	#if (defined(SOIL_MODE) || defined(TANK_MODE))

	/**
	 * At certain time intervals it launches the measurements,
	 * depending on the mode of operation.
	 *
	 * @return void
	 **/
	event void TimerSensor.fired()
	{
		post conductMeasurement();
	}
//	#endif

	/**
	 * It launches sending of measurement data in a regular manner with a predefined frequency.
	 *
	 * @return void
	 */
	event void TimerSender.fired(){
		#ifdef TANK_MODE
		post sendMeasurement();
		#endif

		#ifdef SOIL_MODE
		post sendMeasurement();
		#endif

		#ifdef VALVE_MODE
		post sendMeasurementDataToGateway();
		#endif
	}

	/**
	 * @brief It launches a process of controlling a valve on the basis of stored data.
	 * @details It runs with a predefined frequency.
	 *
	 * @return void
	 */
	event void TimerWateringControl.fired()
	{
		post controlWatering();
	}

	/**
	 * Event triggered by end of starting of radio transceiver.
	 * It makes sure the radio interface is started correctly.
	 *
	 * @return void
	 **/
	event void AMControl.startDone(error_t err)
	{
		if (err == SUCCESS)
		{
			//do nothing
		}
		else
			call AMControl.start();
	}

	/**
	 * Event triggered by end of stopping of radio transceiver.
	 * @details It does nothing.
	 *
	 * @return void
	 */
	event void AMControl.stopDone(error_t err)
	{
		//do nothing
	}

	/**
	 * Event triggered when valve control message is sent to sink.
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return void
	 */
	event void ValveControl_AMSend.sendDone(message_t* msg, error_t error)
	{
		if (msg == &pkt)
		{
			txBusy = FALSE;
		}
	}

	/**
	 * Event triggered when data request message is sent to sink.
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return void
	 */
	event void DataRequestToSink_AMSend.sendDone(message_t* msg, error_t error)
	{
		if (msg == &pkt)
		{
			txBusy = FALSE;
		}
	}

	/**
	 * Event triggered when valve state message is sent.
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return void
	 */
	event void ValveState_AMSend.sendDone(message_t *msg, error_t error){
		if (msg == &pkt)
		{
			txBusy = FALSE;
		}
	}

	/**
	 * Event triggered when humidity message is sent to sink.
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return void
	 */
	event void Humidity_AMSend.sendDone(message_t *msg, error_t error){
		if (msg == &pkt)
		{
			txBusy = FALSE;
		}
	}

	/**
	 * Event triggered when water level message is sent to sink.
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return void
	 */
	event void WaterLevel_AMSend.sendDone(message_t *msg, error_t error){
		if (msg == &pkt)
		{
			txBusy = FALSE;
		}
	}

	/**
	 * Event triggered when system state message is sent to gateway.
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return void
	 */
	event void SystemStateToGateway_AMSend.sendDone(message_t *msg, error_t error){
		if (msg == &pkt)
		{
			txBusy = FALSE;
		}
	}

	/**
	 * Event triggered when data request message is sent to sensor node.
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return void
	 */
	event void DataRequestToSensorNode_AMSend.sendDone(message_t *msg, error_t error)
	{
		if (msg == &pkt)
		{
			txBusy = FALSE;
		}
	}

	/**
	 * Event triggered when "no response" message is sent.
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return void
	 */
	event void NoResponse_AMSend.sendDone(message_t *msg, error_t error){
		if (msg == &pkt)
		{
			txBusy = FALSE;
		}
	}

	/**
	 * Event triggered when system alert message is sent to gateway.
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return void
	 */
	event void SystemAlertToGateway_AMSend.sendDone(message_t *msg, error_t error){
		if (msg == &pkt)
		{
			txBusy = FALSE;
		}
	}

	/**
	 * @brief Event trigger by a timer. It posts a task which sends measurement data forward.
	 * @details It is used to delay message sending and avoid collisions.
	 * However, it does not provides mechanism to eliminate collisions at all or to detect them.
	 * It just reduces the probability, hence increases system performance.
	 *
	 * @return void
	 */
	event void TimerResponse.fired() {
		#ifdef SOIL_MODE
			post sendMeasurement();
		#endif

		#ifdef TANK_MODE
			post sendMeasurement();
		#endif

		#ifdef VALVE_MODE
			post sendMeasurementDataToGateway();
		#endif
	}

	/**
	 * Constructs the message with gathered measurement data from all nodes and sends it to gateway.
	 *
	 * @return void
	 **/
	task void sendMeasurementDataToGateway()
	{
			SystemStateMsgToGateway* txPkt = (SystemStateMsgToGateway*) call SystemStateToGateway_Packet.getPayload(&pkt, sizeof(SystemStateMsgToGateway));
			if(txPkt == NULL)
			{
				return;
			}
			if(call SystemStateToGateway_Packet.maxPayloadLength() < sizeof(SystemStateMsgToGateway))
			{
				return;
			}

			txPkt->water = convWater_uint32_t(water);
			txPkt->valve = (nx_uint8_t)valve;
			txPkt->humidity = convHumidity_uint32_t(humidity);

			//atomic
			//{
				if(!txBusy)
				{
					if(call SystemStateToGateway_AMSend.send(AM_BROADCAST_ADDR, &pkt, sizeof(SystemStateMsgToGateway)) == SUCCESS)
					{
						txBusy = TRUE;
					}
				}
			//}*/
	}

	/**
	 * It would either launch the collection of data from sensor motes
	 * or send the already collected data to the gateway depending on data request message.
	 *
	 * @return void
	 **/
	task void processDataRequestMsgToSink()
	{
		if(data_request_msg_to_sink->renew)
		{
			//gather information and send them back to gateway
			publicMeasurementDataToGateway = TRUE;
			post gatherMeasurementDataFromSensorNodes();
		}
		else
		{
			//send already collected data to gateway
			post sendMeasurementDataToGateway();
		}
	}

	/**
	 * It prepares messages consisting of either the valve state, the humidity or the water level
	 * and sends it.
	 *
	 * @return void
	 **/
	task void sendMeasurement()
	{
		#if defined(VALVE_MODE) || defined (TANK_MODE) || defined(SOIL_MODE)
			#ifdef VALVE_MODE
			ValveStateMsg* txPkt = (ValveStateMsg*) call ValveState_Packet.getPayload(&pkt, sizeof(ValveStateMsg));
			#endif

			#ifdef SOIL_MODE
			HumidityMsg* txPkt = (HumidityMsg*) call Humidity_Packet.getPayload(&pkt, sizeof(HumidityMsg));
			#endif

			#ifdef TANK_MODE
			WaterLevelMsg* txPkt = (WaterLevelMsg*) call WaterLevel_Packet.getPayload(&pkt, sizeof(WaterLevelMsg));
			#endif

			if(txPkt == NULL)
			{
				return;
			}

			#ifdef VALVE_MODE
			if(call ValveState_Packet.maxPayloadLength() < sizeof(ValveStateMsg))
			#endif
			#ifdef SOIL_MODE
			if(call Humidity_Packet.maxPayloadLength() < sizeof(HumidityMsg))
			#endif
			#ifdef TANK_MODE
			if(call WaterLevel_Packet.maxPayloadLength() < sizeof(WaterLevelMsg))
			#endif
			{
				return;
			}
			#ifdef VALVE_MODE
			txPkt->valve = valve;
			#endif
			#ifdef SOIL_MODE
			txPkt->humidity = convHumidity_uint32_t(humidity);
			#endif
			#ifdef TANK_MODE
			txPkt->water = convWater_uint32_t(water);
			#endif
			//txPkt->sender = selfId;

			//atomic
			//{
				if(!txBusy)
				{
					#ifdef VALVE_MODE
					if(call ValveState_AMSend.send(AM_BROADCAST_ADDR, &pkt, sizeof(ValveStateMsg)) == SUCCESS)
					#endif
					#ifdef SOIL_MODE
					if(call Humidity_AMSend.send(AM_BROADCAST_ADDR, &pkt, sizeof(HumidityMsg)) == SUCCESS)
					#endif
					#ifdef TANK_MODE
					if(call WaterLevel_AMSend.send(AM_BROADCAST_ADDR, &pkt, sizeof(WaterLevelMsg)) == SUCCESS)
					#endif
					{
						txBusy = TRUE;
					}
				}
			//}*/
			#endif
	}

	/**
	 * @brief It processes data request message received by a sensor mote.
	 * @details It either post a task conductMeasurement() to sense the real quantity
	 * or starts a TimerResponse to send value already saved in the mote's memory.
	 * This behavior depends on data request message received by the mote.
	 *
	 * @see conductMeasurement()
	 * @see TimerResponse.fired()
	 * @return void
	 */
	task void processDataRequestMsgToSensorNode()
	{
		bool renew_measurement = FALSE;
		#ifdef TANK_MODE
		if(data_request_msg_to_sensor_node->renew & WATER_LEVEL_DATA_SIG)
			renew_measurement = TRUE;
		#endif
		#ifdef SOIL_MODE
		if(data_request_msg_to_sensor_node->renew & HUMIDITY_LEVEL_DATA_SIG)
			renew_measurement = TRUE;
		#endif
		if(data_request_msg_to_sensor_node->renew)
		{
			publicMeasurement = TRUE;
			post conductMeasurement();
		}
		else
		{
			//send already collected data to gateway
			#ifdef SOIL_MODE
			call TimerResponse.startOneShot(TIMER_RESPONSE_S);
			#endif
			#ifdef TANK_MODE
			call TimerResponse.startOneShot(TIMER_RESPONSE_T);
			#endif
		}
	}

	/**
	* @brief It would either send the data already stored in the gateway mote to the serial port
	* or gather the data from the sink.
	*
	* @details The behavior depends on previously received data request message.
	*
	* @return void
	**/
	task void processDataRequestMsgToGateway()
	{
		if(data_request_msg_to_gateway->renew)
		{
			//gather information and send them back to gateway
			publicMeasurementDataToSerial = TRUE;
			post gatherMeasurementDataFromSink();
		}
		else
		{
			//send already collected data to gateway
			post sendMeasurementDataToSerial();
		}
	}

	/**
	 * @brief Event triggered when humidity message is received.
	 *
	 * @details When the message containing the humidity arrives
	 * it extracts the humidity value and converts it into
	 * a double number. Then the humidity value is saved.
	 * The TimerResponse is activated.
	 *
	 * @see TimerReposponse.fired()
	 * @see convHumidity_double()
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return message_t* Pointer to message
	 **/
	event message_t* Humidity_Receive.receive(message_t* msg, void* payload, uint8_t len)
	{
		if(len == sizeof(HumidityMsg))
		{
			HumidityMsg* rxPkt = (HumidityMsg*) payload;
				humidity = convHumidity_double((uint32_t)rxPkt->humidity);
				collectedData |= HUMIDITY_LEVEL_DATA_SIG;
				call Leds.set(collectedData);
				if(collectedData <= data_request_msg_to_sink->renew && collectedData) {
					call TimerResponse.startOneShot(TIMER_RESPONSE_V);
					data_request_msg_to_sink->renew = 0;
					collectedData = 0;
				}
		}
		return msg;
	}

	/**
	 * @brief Event trigger when water level message is received.
	 *
	 * @details When the message containing the water level in the tank arrives
	 * it extracts the water level value and converts it into
	 * a double number. Then the water level value is saved.
	 * The TimerResponse is activated.
	 *
	 * @see TimerResponse.fired()
	 * @see convWater_double()
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return message_t* Pointer to message
	 **/

	event message_t* WaterLevel_Receive.receive(message_t* msg, void* payload, uint8_t len) {
		if(len == sizeof(WaterLevelMsg))
		{
			WaterLevelMsg* rxPkt = (WaterLevelMsg*) payload;
			water = convWater_double((uint32_t)rxPkt->water);
			collectedData |= WATER_LEVEL_DATA_SIG;
			call Leds.set(collectedData);
			if(collectedData <= data_request_msg_to_sink->renew && collectedData) {
				call TimerResponse.startOneShot(TIMER_RESPONSE_V);
			    data_request_msg_to_sink->renew = 0;
			    collectedData = 0;
			}
		}
			return msg;
	}

	/**
	 * @brief Event triggered by receiving valve state message by the sink mote.
	 *
	 * @details When the message containing the state of the valve arrives
	 * it extracts the state value and saves it.
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return message_t* Pointer to message
	 **/
	event message_t* ValveState_Receive.receive(message_t* msg, void* payload, uint8_t len) {
		if(len == sizeof(ValveStateMsg))
		{
			ValveStateMsg* rxPkt = (ValveStateMsg*) payload;
			valve = rxPkt->valve;
		}
		return msg;
	}

	/**
	 * @brief Event triggered by receiving system state message from sink to gateway mote.
	 *
	 * @details When the message containing the state of the system arrives
	 * it extracts the valve state, the amount of humidity as well as the water level.
	 * This packet is sent through the SendMeasurementDatatoGateway task.
	 * After extraction the humidity and the water level are converted in double value.
	 *
	 * @see sendMeasurementDataToGateway()
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return message_t* Pointer to message
	**/
	event message_t* SystemStateToGateway_Receive.receive(message_t* msg, void* payload, uint8_t len) {
		if(len == sizeof(SystemStateMsgToGateway))
		{
			SystemStateMsgToGateway* rxPkt = (SystemStateMsgToGateway*) payload;
			valve = rxPkt->valve;
			water = convWater_double((uint32_t)rxPkt->water);
			humidity = convHumidity_double((uint32_t)rxPkt->humidity);
			post sendMeasurementDataToSerial();
		}
		return msg;
	}

	/**
	 * @brief Event triggered by receiving data request message by a sensor mote.
	 *
	 * @details When the data request arrives the processDataRequestMsgToSensorNode task is called
	 * which would in turn start measurements or send the already measured values.
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return message_t* Pointer to message
	**/
	event message_t* DataRequestToSensorNode_Receive.receive(message_t* msg, void* payload, uint8_t len) {
		if(len == sizeof(DataRequestMsgToSensorNode))
		{
			DataRequestMsgToSensorNode* rxPkt = (DataRequestMsgToSensorNode*) payload;
			data_request_msg_to_sensor_node = rxPkt;
			post processDataRequestMsgToSensorNode();
		}
		return msg;
	}

	/**
	 * @brief Event triggered by receiving valve control message.
	 *
	 * @details When a message for controlling the valve arrives the value would be extracted.
	 * If it is 0 then the valve should be closed by calling the corresponding command.
	 * Otherwise it would be opened depending on the value in the received message.
	 *
	 * @see Valve.open(uint8_t)
	 * @see Valve.close()
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return message_t* Pointer to message
	 **/
	event message_t* ValveControl_Receive.receive(message_t* msg, void* payload, uint8_t len)	{
		#ifdef VALVE_MODE
		if(len == sizeof(ValveControlMsgToValve))
		{
			ValveControlMsgToValve *rxPkt = (ValveControlMsgToValve*) payload;
			if(!rxPkt->valve)
				call Valve.close();
			else
				call Valve.open((uint8_t)rxPkt->valve);
		}
		#endif
		return msg;
	}

	/**
	 * @brief Event triggered by receiving system alert message from sink mote by a gateway mote.
	 *
	 * @details When an alert arrives at the Gateway the task sendSystemAlertMsgToSerial is called.
	 * In other words the alert would be sent on the serial interface.
	 *
	 * @see sendSystemAlertMsgToSerial()
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return message_t* Pointer to message
	 **/
	event message_t* SystemAlertToGateway_Receive.receive(message_t* msg, void* payload, uint8_t len) 	{
		if(len == sizeof(SystemAlertMsgToGateway))
		{
			SystemAlertMsgToGateway* rxPkt = (SystemAlertMsgToGateway*) payload;
			system_alert_msg_to_gayeway = rxPkt;
			post sendSystemAlertMsgToSerial();
		}
		return msg;
	}

	/**
	 * @brief Event triggered by receiving data request message by the sink mote.
	 *
	 * @details When the data request arrives the processDataRequestMsgToSink task is called which would in turn
	 * gather measured data from sensors or would send gathered data.
	 *
	 * @see processDataRequestMsgToSink
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return message_t* Pointer to message
	 **/
	event message_t* DataRequestToSink_Receive.receive(message_t* msg, void* payload, uint8_t len)
	{
		if(len == sizeof(DataRequestMsgToSink))
		{
			DataRequestMsgToSink* rxPkt = (DataRequestMsgToSink*) payload;
			data_request_msg_to_sink = rxPkt;
			#ifdef VALVE_MODE
			post processDataRequestMsgToSink();
			#endif
		}
		return msg;
	}

	/**
	 * @brief Event triggered by receiving "no response" message by a mote.
	 *
	 * @details Not implemented. Just do nothing. Such messages are not sent anyway.
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return message_t* Pointer to message
	 **/
	event message_t* NoResponse_Receive.receive(message_t* msg, void* payload, uint8_t len)
	{
		return msg;
	}

	/**
	 * It verifies if value pass into a function corresponds to an alarm value of the quantity
	 * (either humidity or water level depending on the mote type).
	 *
	 * @param value double Value to be checked
	 * @return boolean True if alarm should be raised, false otherwise.
	 */
	bool sensorAlarm(double value)
	{
		return 0;

		#ifdef SOIL_MODE
		if(value < SOIL_HUMIDITY_THRESHOLD)
			return TRUE;
		#endif
		#ifdef TANK_MODE
		if(value < WARN_WATER_LEVEL)
			return TRUE;
		#endif

		return FALSE;
	}

	//#if (defined(SOIL_MODE) || defined(TANK_MODE))

	/**
	 * When the sensor is read, depending on which sensor it is, the humidity value
	 * or the water level would be assigned to the sensor data.
	 * If the values are below the threshold defined, a timer, Timer Response,  would be activated
	 * after which the measured data would be sent.
	 *
	 * @see TimerResponse.fired()
	 *
	 * @param result error_t Result of reading from sensor
	 * @param value double Value read from the sensor
	 **/
	event void SensorRead.readDone(error_t result, double value)
	{
		if (result == SUCCESS)
		{
	    	//process and store sensor data
    		sensorData = value;
    		#ifdef SOIL_MODE
    		humidity = sensorData;
    		#endif
    		#ifdef TANK_MODE
    		water = sensorData;
    		#endif

    		if(publicMeasurement || sensorAlarm(sensorData))
    		{
    			publicMeasurement = FALSE;
    			#ifdef SOIL_MODE
				call TimerResponse.startOneShot(TIMER_RESPONSE_S);
				#endif
				#ifdef TANK_MODE
				call TimerResponse.startOneShot(TIMER_RESPONSE_T);
				#endif
    		}
    	}
	}
	//#endif

	#ifdef VALVE_MODE
	/**
	 * The value of the valve would be assigned to 0 when the valve is closed.
	 *
	 * @param result error_t Result of closing valve operation
	 **/
	event void Valve.closeDone(error_t result)
	{
		valve = 0;
	}

	/**
	 * When the valve is opened its status would be saved as valve.
	 *
	 * @param result error_t Result of opening valve operation
	 **/
	event void Valve.openDone(error_t result)
	{
		valve = call Valve.getValvePosition();
	}
	#endif

	/**
	 * Event triggered by finish of starting procedure of serial communication support.
	 *
	 * @param error error_t Result of an attempt to run serial control. Expected value: SUCCESS.
	 */
	event void SerialControl.startDone(error_t error){
		call Leds.led0On();
	}

	/**
	 * Event triggered by finish of stopping procedure of serial communication support.
	 *
	 * @param error error_t Result of an attempt to stop serial control. Expected value: SUCCESS.
	 */
	event void SerialControl.stopDone(error_t error){
	}

	/**
	 * Task calling sensor components to read their values.
	 * Actual sensor component called depends on the wiring, thus on flag passed to compiler.
	 *
	 * @see SensorRead.readDone()
	 *
	 * @return void
	 */
	task void conductMeasurement()
	{
		#ifdef TANK_MODE
		call SensorRead.read();
		#endif

		#ifdef SOIL_MODE
		call SensorRead.read();
		#endif
	}

	/**
	 * Task that sends measurement data stored at mote's memory to serial port.
	 *
	 * @return void
	 */
	task void sendMeasurementDataToSerial()
	{
		SystemStateMsgToUSB* serialTxPkt = (SystemStateMsgToUSB*) call SerialPacket.getPayload(&serial_pkt, sizeof(SystemStateMsgToUSB));
		if(serialTxPkt == NULL)
		{
			return;
		}
		if(call SerialPacket.maxPayloadLength() < sizeof(SystemStateMsgToUSB))
		{
			return;
		}


		serialTxPkt->water = convWater_uint32_t(water);
		serialTxPkt->valve = (nx_uint8_t)valve;
		serialTxPkt->humidity = convHumidity_uint32_t(humidity);

		//atomic
		//{
			if(!serialTxBusy)
			{
				if(call SystemStateToUSB_SerialMessageSend.send(AM_BROADCAST_ADDR, &serial_pkt, sizeof(SystemStateMsgToUSB)) == SUCCESS)
				{
					serialTxBusy = TRUE;
				}
			}
		//}*/
	}

	/**
	 * Process the message to be sent on the serial interface with the value
	 * representing state of the valve.
	 *
	 * @return void
	 **/
	task void processValveControlMsgToGateway()
	{
		ValveControlMsgToValve* txPkt = (ValveControlMsgToValve*) call ValveControl_Packet.getPayload(&pkt, sizeof(ValveControlMsgToValve));
		if(txPkt == NULL)
		{
			return;
		}
		if(call ValveControl_Packet.maxPayloadLength() < sizeof(ValveControlMsgToValve))
		{
			return;
		}

		txPkt->valve = valve_control_to_gateway->valve;

		//atomic
		//{
			if(!txBusy)
			{
				if(call ValveControl_AMSend.send(AM_BROADCAST_ADDR, &pkt, sizeof(ValveControlMsgToValve)) == SUCCESS)
				{
					txBusy = TRUE;
				}
			}
		//}
	}

	/**
	 * Function sending data request message to sensor node.
	 *
	 * @param node Id of node which the message should be sent to
	 * @return void
	 */
	void gatherMeasurementDataFromSensorNode(uint8_t node)
	{
		uint16_t addr = 0xffff;
		DataRequestMsgToSensorNode* txPkt = (DataRequestMsgToSensorNode*) call DataRequestToSensorNode_Packet.getPayload(&pkt, sizeof(DataRequestMsgToSensorNode));
		if(txPkt == NULL)
		{
			return;
		}
		if(call DataRequestToSensorNode_Packet.maxPayloadLength() < sizeof(DataRequestMsgToSink))
		{
			return;
		}

		txPkt->renew = data_request_msg_to_sink->renew;

		if(node == WATER_LEVEL_DATA_SIG)
			addr = TANK_NODE_ID;
		else if(node == HUMIDITY_LEVEL_DATA_SIG)
			addr = HUMIDITY_NODE_ID;
		//atomic
		//{

			if(!txBusy)
			{
				//!!!!!!!!!!! NO Broadcast      --->>> UNICAST
				if(call DataRequestToSensorNode_AMSend.send(addr, &pkt, sizeof(DataRequestMsgToSensorNode)) == SUCCESS)
				{
					txBusy = TRUE;
				}
			}
		//}*/
	}

	/**
	 * Sends a data request to either the humidity node or the water level node.
	 * This message is transmitted only to the corresponding node and so is a unicast.
	 * The address of the corresponding node is calculated through the gatherMeasurementDataFromSensorNode
	 * function.
	 *
	 * @see gatherMeasurementDataFromSensorNode(uint8_t node)
	 * @return void
	 **/
	task void gatherMeasurementDataFromSensorNodes()
	{
		collectedData = 0;
		if(data_request_msg_to_sink->requested_data & WATER_LEVEL_DATA_SIG)
			gatherMeasurementDataFromSensorNode(WATER_LEVEL_DATA_SIG);

		if(data_request_msg_to_sink->requested_data & HUMIDITY_LEVEL_DATA_SIG)
			gatherMeasurementDataFromSensorNode(HUMIDITY_LEVEL_DATA_SIG);
	}

	/**
	 * Task that sends data request message to the sink/valve mote. It is intended to be run when data
	 * request message (with flag to renew the measurement data) is received by the gateway.
	 *
	 * @return void
	 */
	task void gatherMeasurementDataFromSink()
	{
		DataRequestMsgToSink* txPkt = (DataRequestMsgToSink*) call DataRequestToSink_Packet.getPayload(&pkt, sizeof(DataRequestMsgToSink));
		if(txPkt == NULL)
		{
			return;
		}
		if(call DataRequestToSink_Packet.maxPayloadLength() < sizeof(DataRequestMsgToSink))
		{
			return;
		}

		txPkt->requested_data = data_request_msg_to_gateway->requested_data;
		txPkt->renew = data_request_msg_to_gateway->renew;

		//atomic
		//{
			if(!txBusy)
			{
				if(call DataRequestToSink_AMSend.send(AM_BROADCAST_ADDR, &pkt, sizeof(DataRequestMsgToSink)) == SUCCESS)
				{
					txBusy = TRUE;
				}
			}
		//}
	}

	/**
	 * Event triggered after finishing of sending system state message to serial port/USB.
	 *
	 * @param msg message_t Pointer to message
	 * @param error errot_t Data sending status. Expected value: SUCCESS
	 * @return void
	 */
	event void SystemStateToUSB_SerialMessageSend.sendDone(message_t *msg, error_t error) {
		if (msg == &serial_pkt)
		{
			serialTxBusy = FALSE;
		}
	}

	/**
	 * Event triggered after finishing of sending system alert message to serial port/USB.
	 *
	 * @param msg message_t Pointer to message
	 * @param error errot_t Data sending status. Expected value: SUCCESS
	 * @return void
	 */
	event void SystemAlertToUSB_SerialMessageSend.sendDone(message_t *msg, error_t error){
		if (msg == &serial_pkt)
		{
			serialTxBusy = FALSE;
		}
	}

	/**
	 * @brief Event triggered by arrival of data request message to the gateway via USB.
	 * @details Message is saved and a processDataRequestMsgtoGateway task is posted.
	 *
	 * @see processDataRequestMsgToGateway()
	 *
	 * @param msg message_t* Pointer to message
	 * @param error error_t Status
	 * @return message_t* Pointer to message
	 **/
	event message_t* DataRequestToGateway_SerialReceive.receive(message_t *msg, void *payload, uint8_t len){
		if(len == sizeof(DataRequestMsgToGateway))
		{
			DataRequestMsgToGateway* rxPkt = (DataRequestMsgToGateway*) payload;
			data_request_msg_to_gateway = rxPkt;
			post processDataRequestMsgToGateway();
		}

		return msg;
	}

	/**
	 * @brief Event signaled after receiving Valve Control Message from serial port.
	 * @details When the user sends a valve control message to the gateway,
	 * a processValveControlMsgToGateway task is posted.
	 *
	 * @see processValveControlMsgToGateway()
	 *
	 * @param message_t* msg Pointer to message
	 * @param void*	payload Pointer to payload which can be cast to pointer to received packet
	 * @param uint8_t len Length of received message
	 * @return message_t* Pointer to message
	 **/
	event message_t* ValveControlToGateway_SerialReceive.receive(message_t *msg, void *payload, uint8_t len) {

		if(len == sizeof(ValveControlMsgToGateway))
		{
			ValveControlMsgToGateway* rxPkt = (ValveControlMsgToGateway*) payload;
			valve_control_to_gateway = rxPkt;
			post processValveControlMsgToGateway();
		}
		return msg;
	}

	/**
	 * @brief Sends an alert message from sink to the gateway.
	 * @details Alert type is set before by other task. This task only sends the message.
	 *
	 * @return void
	 **/
	task void sendSystemAlertMsgToGateway()
	{
		SystemAlertMsgToGateway* txPkt = (SystemAlertMsgToGateway*) call SystemAlertToGateway_Packet.getPayload(&pkt, sizeof(SystemAlertMsgToGateway));
		if(txPkt == NULL)
		{
			return;
		}
		if(call SystemAlertToGateway_Packet.maxPayloadLength() < sizeof(SystemAlertMsgToGateway))
		{
			return;
		}

		txPkt->alert = alert_type;

		//atomic
		//{
			if(!txBusy)
			{
				if(call SystemAlertToGateway_AMSend.send(AM_BROADCAST_ADDR, &pkt, sizeof(SystemAlertMsgToGateway)) == SUCCESS)
				{
					txBusy = TRUE;
				}
			}
		//}
	}

	/**
	 * @brief Sends an alert message to serial port (USB).
	 * @details Alert is obtained from previously received message from sink.
	 *
	 * @return void
	 **/
	task void sendSystemAlertMsgToSerial()
	{
		SystemAlertMsgToUSB* serialTxPkt = (SystemAlertMsgToUSB*) call SerialPacket.getPayload(&serial_pkt, sizeof(SystemAlertMsgToUSB));
		if(serialTxPkt == NULL)
		{
			return;
		}
		if(call SerialPacket.maxPayloadLength() < sizeof(SystemAlertMsgToUSB))
		{
			return;
		}

		serialTxPkt->alert = system_alert_msg_to_gayeway->alert;

		//atomic
		//{
			if(!serialTxBusy)
			{
				if(call SystemAlertToUSB_SerialMessageSend.send(AM_BROADCAST_ADDR, &serial_pkt, sizeof(SystemAlertMsgToUSB)) == SUCCESS)
				{
					serialTxBusy = TRUE;
				}
			}
		//}
	}

	/**
	 * @brief Task containing valve/watering control algorithm.
	 * @details If the water level in the tank is lower than the threshold (warning level) an alarm message would be sent.
	 * If the humidity in the soil is low and there is water in tank (more than minimum level), the valve would be opened.
	 * The ratio of actual humidity and threshold level controls valve throughput.
	 * The valve is closed in case of too low water level (below minimum level) or humidity higher than threshold value.
	 * It uses leds to indicate system state.
	 *
	 * @see TimerWateringControl.fired()
	 * @return void
	 **/
	task void controlWatering()
	{
		#ifdef VALVE_MODE
		uint8_t previous_alert_type = alert_type;
		alert_type = NO_ALERT;

		call Leds.set(0);
		if(water < WARN_WATER_LEVEL && water > MIN_WATER_LEVEL)

		{
			//water level is below warning level
			//produce warning message
			alert_type = WATER_BELOW_WARN;
			call Leds.led2On();
		}

		if(humidity < SOIL_HUMIDITY_THRESHOLD)
		{
			//soil is too dry
			call Leds.led0On();
			if(water >= MIN_WATER_LEVEL)
			{
				//there is water in tank
				double dryness_severity = (double)humidity/SOIL_HUMIDITY_THRESHOLD;
				if(dryness_severity < 0.2)
					call Valve.openFull();
				else if(dryness_severity < 0.8)
					call Valve.open(200);
				else if(dryness_severity < 0.85)
					call Valve.open(150);
				else if(dryness_severity < 0.9)
					call Valve.open(90);
				else if(dryness_severity < 1.0)
					call Valve.open(40);

				call Leds.led1On();
			}
			if(water < MIN_WATER_LEVEL)
			{
				//water level is below alarm level and soil is dry
				//produce severe alarm message
				call Valve.close();
				alert_type = SOIL_DRY_AND_WATER_BELOW_MIN;
			}
		}
		else //humidity >= SOIL HUMIDITY THRESHOLD
		{
			call Valve.close();

			if(water < MIN_WATER_LEVEL)
			{
				//water level is below warning level
				//produce alarm message
				alert_type = WATER_BELOW_MIN;
			}
			else if(water >= WARN_WATER_LEVEL) {
				//water is above warn level and soil is humid enough
				alert_type = NO_ALERT;
			}
		}
		if(alert_type != previous_alert_type)
			post sendSystemAlertMsgToGateway();

		#endif
	}

	/**
	 * Event signaled after reading from A/D converter
	 *
	 * @param channel uint8_t Number of analog channel that was sampled by the A/D converter
	 * @param value uint8_t Value return by the ADC with respect to reference voltage
	 * @return void
	 */
	async event void SBoard.readDoneADC(uint8_t channel, double value){
	}

	/**
	 * Event signaled after reading from SBoard IO port
	 *
	 * @param value uint8_t Value of IO port return by SBoard component
	 * @return void
	 */
	async event void SBoard.readDoneIO(uint8_t){
	}
}